﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.Common;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Text;
using Mk.CommonMethod;
using log4net;

[assembly: log4net.Config.XmlConfigurator(Watch = true)]
namespace ImportTimeCardData
{
    public class Program
    {
        static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

        static readonly DBConnection DbConnection = new DBConnection
        {
            ConnectionString = ConfigurationManager.AppSettings["ConnectionString"],
            ConnectionType = ConfigurationManager.AppSettings["ConnectionType"]
        };
        static void Main(string[] args)
        {
            try
            {
                ImportTimeCardInfo();
                if(args.Length == 3)
                {
                    ReviseTimeInfo(args);
                }
                CaculateAttendance();
                UpdateCardInfoStatus();
                Console.WriteLine("Run Successfully!");
                Log.Info("Run Successfully!");
            }
            catch (Exception ex)
            {
                Log.Error(ex.StackTrace);
            }
        }

        static void ReviseTimeInfo(string[] args)
        {
            string action = args[0];
            if (string.Compare(action, "Revise", true, CultureInfo.InvariantCulture) == 0)
            {
                DateTime fromDate = DateTime.ParseExact(args[1], "yyyyMMdd", null);
                DateTime toDate = DateTime.ParseExact(args[2], "yyyyMMdd", null);

                string sql =
                    "update timecardinfo set status='0' where TakenDateTime >='{0} 00:00:00' and TakenDateTime<='{1} 23:59:59'";
                sql = string.Format(sql, fromDate.ToString("yyyy-MM-dd"), toDate.ToString("yyyy-MM-dd"));
                DbConnection.ExecuteSql(sql);
            }
        }
        static void UpdateCardInfoStatus()
        {
            Console.WriteLine("Updating TimeCard Info Status...");
            Log.Info("Updating TimeCard Info Status...");
            const string sql = "update TimeCardInfo set status=1 where status=0";
            DbConnection.ExecuteSql(sql);
        }
        static void ImportTimeCardInfo()
        {
            Console.WriteLine("Importing TimeCard Info...");
            Log.Info("Importing TimeCard Info...");
            string sourceDataPath = ConfigurationManager.AppSettings["SourceData"];
            string sourceDataBackupPath = ConfigurationManager.AppSettings["SourceDataBackup"];
            if (!Directory.Exists(sourceDataBackupPath))
            {
                Directory.CreateDirectory(sourceDataBackupPath);
            }
            var files = Directory.GetFiles(sourceDataPath);
            foreach (var file in files)
            {
                string fileName = Path.GetFileName(file);
                try
                {
                    if (string.Compare(Path.GetExtension(file), ".txt", StringComparison.InvariantCultureIgnoreCase) != 0)
                    {
                        continue;
                    }
                    var sourceData = File.ReadAllText(file, Encoding.UTF8);
                    var sourceDataLines = sourceData.Split(System.Environment.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
                    var readData = new List<string>();
                    foreach (var sourceDataLine in sourceDataLines)
                    {
                        if (readData.Contains(sourceDataLine))
                        {
                            continue;
                        }
                        readData.Add(sourceDataLine);
                        var timeCardInfo = new TimeCardInfo()
                        {
                            CardNo = sourceDataLine.Substring(0, 6),
                            TakenDateTime = DateTime.ParseExact(sourceDataLine.Substring(6, 13), "HH:mmyyyyMMdd", null),
                            DeviceNo = sourceDataLine.Substring(19),
                            SourceInfo = sourceDataLine
                        };
                        InsertIntoTimeCard(timeCardInfo);
                    }
                    string fileMoveTo = Path.Combine(sourceDataBackupPath, fileName);
                    if(File.Exists(fileMoveTo))
                    {
                        File.Delete(fileMoveTo);
                    }
                    File.Move(file, fileMoveTo);
                    Log.Info("Import File " + fileName + " Successfully!");
                }
                catch (Exception ex)
                {
                    Log.Error("Import File " + fileName + " Error! Exception StackTrace:" + ex.StackTrace);
                }
            }
        }

        static void InsertIntoTimeCard(TimeCardInfo cardInfo)
        {
            string sqlDelete = "delete from TimeCardInfo where SourceInfo='{0}'";
            sqlDelete = string.Format(sqlDelete, cardInfo.SourceInfo);
            DbConnection.ExecuteSql(sqlDelete);
            string sql =
                @"INSERT INTO [TimeCardInfo]
                   ([CardNo]
                   ,[TakenDateTime]
                   ,[DeviceNo]
                   ,[Status]
                   ,[InsertDateTime]
                   ,[SourceInfo])VALUES
                   ('{0}'
                   ,'{1}'
                   ,'{2}'
                   ,0
                   ,getdate()
                   ,'{3}')";
            sql = string.Format(sql, cardInfo.CardNo, cardInfo.TakenDateTime, cardInfo.DeviceNo, cardInfo.SourceInfo);
            DbConnection.ExecuteSql(sql);
        }

        static void CaculateAttendance()
        {
            Console.WriteLine("Caculate Attendance Info...");
            Log.Info("Caculate Attendance Info...");
            var list = GetCaculateAttendance();
            if (list != null)
            {
                Console.WriteLine("Saving Attendance Info...");
                Log.Info("Saving Attendance Info...");
                InsertIntoStaffAttendance(list);   
            }
        }
        static List<StaffAttendance> GetCaculateAttendance()
        {
            try
            {
                var staffSchedule = GetStaffSchedule();
                if(staffSchedule == null)
                {
                    return null;
                }
                const string sql = @"SELECT [CardNo]
                            ,[TakenDateTime]
                            ,[DeviceNo]
                            ,[Status]
                            ,[InsertDateTime]
                            ,[SourceInfo]
                        FROM [TimeCardInfo] where status=0";
                var timeCardInfo = DbConnection.DataSet(sql).Tables[0];
                var list = new List<StaffAttendance>();
                foreach (DataRow row in staffSchedule.Rows)
                {
                    var staffNo = row["StaffNo"].ToString();
                    var cardNo = row["cardno"].ToString();
                    var onDutyTime = (DateTime)row["OnDutyTime"];
                    var offDutyTime = (DateTime)row["OffDutyTime"];
                    var workingdate = (DateTime)row["workingdate"];

                    var staffAttendance = new StaffAttendance
                    {
                        StaffNo = staffNo,
                        AttendanceDate = workingdate,
                        OnDutyTime = GetTimeCardTime(onDutyTime, timeCardInfo, cardNo,true),
                        OffDutyTime = GetTimeCardTime(offDutyTime, timeCardInfo, cardNo,false)
                    };
                    list.Add(staffAttendance);
                }
                return list;
            }
            catch (Exception ex)
            {
                Log.Error("GetCaculateAttendance Error:" + ex.StackTrace);
                throw;
            }
        }

        private static void InsertIntoStaffAttendance(List<StaffAttendance> data)
        {
            foreach (var sa in data)
            {
                try
                {


                    string delete = string.Format(
                        "delete from StaffAttendance where StaffNo='{0}' and AttendanceDate='{1}'", sa.StaffNo,
                        sa.AttendanceDate.Date.ToString("yyyy-MM-dd"));
                    DbConnection.ExecuteSql(delete);

                    string insert = @"INSERT INTO [StaffAttendance]
                               ([StaffNo]
                               ,[AttendanceDate]
                               ,[OnDutyTime]
                               ,[OffDutyTime])
                                VALUES
                               ('{0}'
                               ,'{1}'
                               ,{2}
                               ,{3})";
                    string onDutyTime = "'" + sa.OnDutyTime.ToString("yyyy-MM-dd HH:mm:ss") + "'";
                    if (sa.OnDutyTime == DateTime.MinValue)
                    {
                        onDutyTime = "null";
                    }
                    string offDutyTime = "'" + sa.OffDutyTime.ToString("yyyy-MM-dd HH:mm:ss") + "'";
                    if (sa.OffDutyTime == DateTime.MinValue)
                    {
                        offDutyTime = "null";
                    }
                    insert = string.Format(insert, sa.StaffNo, sa.AttendanceDate.ToString("yyyy-MM-dd"), onDutyTime, offDutyTime);
                    DbConnection.ExecuteSql(insert);
                }
                catch (Exception ex)
                {
                    Log.Error("InsertIntoStaffAttendance Error:" + ex.StackTrace);
                }
            }
        }

        private static DateTime GetTimeCardTime(DateTime scheduleTime, DataTable timeCardInfo, string cardNo, bool isOnduty)
        {
            double bufferHourForward = 4;
            if (!string.IsNullOrEmpty(ConfigurationManager.AppSettings["BufferHourForward"]))
            {
                bufferHourForward = double.Parse(ConfigurationManager.AppSettings["BufferHourForward"]);
            }
            double bufferHourBackward = 1;
            if (!string.IsNullOrEmpty(ConfigurationManager.AppSettings["BufferHourBackward"]))
            {
                bufferHourBackward = double.Parse(ConfigurationManager.AppSettings["BufferHourBackward"]);
            }
            DateTime onDutyTimeStart;
            DateTime onDutyTimeEnd;
            if(isOnduty)
            {
                onDutyTimeStart = scheduleTime.AddHours(-bufferHourForward);
                onDutyTimeEnd = scheduleTime.AddHours(bufferHourBackward);
            }
            else
            {
                onDutyTimeStart = scheduleTime.AddHours(-bufferHourBackward);
                onDutyTimeEnd = scheduleTime.AddHours(bufferHourForward);
            }
            
            string filterOn = string.Format("TakenDateTime>='{0}' and TakenDateTime<='{1}' and CardNo='{2}'", onDutyTimeStart, onDutyTimeEnd, cardNo);
            var dataRowsOn = timeCardInfo.Select(filterOn);
            if (dataRowsOn.Length > 0)
            {
                return (DateTime)dataRowsOn[0]["TakenDateTime"];
            }
            return DateTime.MinValue;
        }

        static DataTable GetStaffSchedule()
        {
            string sql = "select max(takendatetime) as maxDate,min(takendatetime) as minDate from [TAW].[dbo].[TimeCardInfo] where status=0";
            var timeCardInfoDate = DbConnection.DataSet(sql).Tables[0];
            if (timeCardInfoDate.Rows[0]["maxDate"] == null || string.IsNullOrEmpty(timeCardInfoDate.Rows[0]["maxDate"].ToString()))
            {
                return null;
            }
            var maxDate = ((DateTime)timeCardInfoDate.Rows[0]["maxDate"]).AddDays(1);
            var minDate = ((DateTime)timeCardInfoDate.Rows[0]["minDate"]).AddDays(-1);

            string sqlQuery =
                @"select a.staffno,c.cardno,a.workingdate,b.fromtime,b.totime,b.totaltime from WorkerShiftSchedule a
                  inner join WorkingShift b on a.workingShift = b.id
                  inner join namelist c on a.staffno=c.staffno where a.workingdate>='{0}' and a.workingdate<='{1}'";
            sqlQuery = string.Format(sqlQuery, minDate.Date.ToString("yyyy-MM-dd"), maxDate.Date.ToString("yyyy-MM-dd"));
            var staffScheduleInfo = DbConnection.DataSet(sqlQuery).Tables[0];
            staffScheduleInfo.Columns.Add("OnDutyTime", typeof(DateTime));
            staffScheduleInfo.Columns.Add("OffDutyTime", typeof(DateTime));
            foreach (DataRow row in staffScheduleInfo.Rows)
            {
                var date = ((DateTime)row["workingdate"]).ToString("yyyy-MM-dd");
                var fromTime = row["fromtime"].ToString();
                var toTime = row["totime"].ToString();

                var onDutyTime = DateTime.ParseExact(date + " " + fromTime, "yyyy-MM-dd HH:mm:ss", null);
                var offDutyTime = DateTime.ParseExact(date + " " + toTime, "yyyy-MM-dd HH:mm:ss", null);
                if (offDutyTime < onDutyTime)
                {
                    offDutyTime = offDutyTime.AddDays(1);
                }
                row["OnDutyTime"] = onDutyTime;
                row["OffDutyTime"] = offDutyTime;
            }
            return staffScheduleInfo;
        }
    }
}
